package cyclestats

import (
	"context"
	"fmt"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
	"kratos-layout/pkg/logger"
	"time"
)

const (
	collectionName = "cycle_stats"
)

type CycleStat struct {
	Timestamp time.Time     `json:"timestamp" bson:"timestamp"`
	Type      string        `json:"type" bson:"type"`
	TimeUnit  time.Duration `json:"time_unit" bson:"time_unit"`
	Number    int64         `json:"number,omitempty" bson:"number"`
	Total     int64         `json:"total,omitempty" bson:"total"`
	Values    map[int]int64 `json:"values,omitempty" bson:"values"`
}

// GetCycleStatsByCond 获取满足条件的数据
func (s *Data) GetCycleStatsByCond(ctx context.Context, condition bson.M, page, pageSize int) ([]*CycleStat, error) {
	var entities []*CycleStat

	if condition == nil {
		condition = bson.M{}
	}
	findOpt := options.Find()
	findOpt.SetLimit(int64(pageSize))
	findOpt.SetSkip(int64((page - 1) * pageSize))
	cur, err := s.DB.Collection(collectionName).Find(ctx, condition, findOpt)
	if err != nil {
		logger.Errorf("GetCycleStatsByCond err, err is %v", err)
		return nil, err
	}
	defer func(cur *mongo.Cursor, ctx context.Context) {
		_ = cur.Close(ctx)
	}(cur, ctx)

	for cur.Next(ctx) {
		entity := &CycleStat{}
		err := cur.Decode(entity)
		if err != nil {
			logger.Errorf("GetCycleStatsByCond err, err is %v", err)
			return nil, err
		}

		entities = append(entities, entity)
	}

	return entities, nil
}

func (s *Data) CreateIndexes() error {
	// 创建复合索引的多个字段和排序方式
	indexModels := []mongo.IndexModel{
		{
			Keys: bson.D{
				{"timestamp", 1}, // 1表示升序，-1表示降序
				{"type", 1},
				{"time_unit", 1},
			},
			Options: options.Index().SetBackground(true),
		},
	}

	_, err := s.DB.Collection(collectionName).Indexes().CreateMany(context.Background(), indexModels)
	if err != nil {
		logger.Errorf("CreateIndexes err, err is %v", err)
	}

	return err
}

func (s *Data) AddStats(timeUnit time.Duration, timestamp time.Time, statsType string, value int64) error {
	index := timeIndex(timeUnit, timestamp)
	tsTruncated := timeTruncate(timeUnit, timestamp)
	filter := bson.M{
		"timestamp": tsTruncated,
		"type":      statsType,
		"time_unit": timeUnit,
	}

	update := bson.M{
		"$set": bson.M{fmt.Sprintf("values.%d", index): value},
		"$inc": bson.M{"number": 1, "total": value},
	}

	opts := options.Update().SetUpsert(true)
	_, err := s.DB.Collection(collectionName).UpdateMany(context.Background(), filter, update, opts)
	return err
}

func (s *Data) DeleteExpiredStats(timeUnit time.Duration, duration time.Duration) error {
	_, err := s.DB.Collection(collectionName).DeleteMany(
		context.Background(),
		bson.M{"timestamp": bson.M{"$lt": time.Now().Add(-duration)}, "time_unit": timeUnit},
		nil,
	)

	if err != nil {
		logger.Errorf("DeleteExpiredStats error: %v", err)
	}

	return err
}
